#pragma once

#include <assert.h>
#include <any>
#include <typeindex>
#include <unordered_map>

#include <memory>
#include <mutex>
#include <vector>

namespace qbe::ecs {

class Bus {
 public:
  Bus() { mutex_ptr = std::make_shared<std::mutex>(); }

  template <typename T>
  void send(const T& t) {
    std::unique_lock grd(*mutex_ptr);

    auto& any = send_que.emplace_back();
    any = std::make_any<T>(t);
  }

  template <typename T>
  void send(const std::vector<T>& tarr) {
    std::unique_lock grd(*mutex_ptr);

    send_que.reserve(send_que.size() + tarr.size());
    for (auto& t : tarr) {
      auto& any = send_que.emplace_back();
      any = std::make_any<T>(t);
    }
  }

  void sendToBus(Bus& bus) {
    std::scoped_lock grd(*mutex_ptr, *bus.mutex_ptr);

    bus.receive_que.insert(bus.receive_que.end(), send_que.begin(), send_que.end());
    send_que.clear();
  }

  template <typename T>
  std::vector<T> receive() {
    std::unique_lock grd(*mutex_ptr);

    std::vector<T> res;
    std::type_index id = std::type_index(typeid(T));

    for (auto& any : receive_que) {
      if (std::type_index(any.type()) == id) {
        auto& t = res.emplace_back();
        t = std::any_cast<T>(any);
      }
    }

    return res;
  }

  std::vector<std::any> receiverAll() {
    std::unique_lock grd(*mutex_ptr);

    return receive_que;
  }

  void receiveFromBus(Bus& bus) {
    std::scoped_lock grd(*mutex_ptr, *bus.mutex_ptr);

    receive_que.insert(receive_que.end(), bus.send_que.begin(), bus.send_que.end());
    bus.send_que.clear();
  }

  template <typename T>
  void clearReceive() {
    std::unique_lock grd(*mutex_ptr);

    std::vector<T> res;
    std::type_index id = std::type_index(typeid(T));

    auto it = receive_que.begin();
    while (it != receive_que.end()) {
      if (std::type_index(it->type()) == id) {
        it = receive_que.erase(it);
      }
      if (it != receive_que.end())
        it++;
    }
  }

  void clearAllReceive() {
    std::unique_lock grd(*mutex_ptr);

    receive_que.clear();
  }

  std::vector<std::any>& getSendQue() { return send_que; }
  std::vector<std::any>& getReceiveQue() { return receive_que; }

 private:
  std::shared_ptr<std::mutex> mutex_ptr;

  std::vector<std::any> send_que;
  std::vector<std::any> receive_que;

  friend class TransferStation;
};

template <typename T>
struct is_bus : public std::false_type {};

template <>
struct is_bus<Bus> : public std::true_type {};

template <>
struct is_bus<Bus&> : public std::true_type {};

}  // namespace qbe::ecs